package com.fjnu.aop;

import com.fjnu.annotation.NPECatch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; // 必须加@Component

import java.text.SimpleDateFormat;
import java.util.Date;

@Aspect // 标记为 AOP 切面
@Component // 交给 Spring 管理
public class DaoAopAspect {
    private static final Logger logger = LoggerFactory.getLogger(DaoAopAspect.class);
    // 线程安全的日期格式化（避免 SimpleDateFormat 线程安全问题）
    private static final ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(
            () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    );

    // 切入点1：匹配 DAO 层所有方法（com.fjnu.dao 下所有类的所有方法）
    @Pointcut("execution(* com.fjnu.dao..*(..))")
    public void daoMethodPointcut() {}

    // 切入点2：匹配类上有 @NPECatch 注解的类
    @Pointcut("@within(com.fjnu.annotation.NPECatch)")
    public void withinNPECatchPointcut() {}

    // 切入点3：匹配方法上有 @NPECatch 注解的方法
    @Pointcut("@annotation(com.fjnu.annotation.NPECatch)")
    public void annotationNPECatchPointcut() {}

    // 环绕通知：记录 DAO 方法执行日志（确保 finally 块中计算耗时，避免异常时日志丢失）
    @Around("daoMethodPointcut()")
    public Object logDaoExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = joinPoint.proceed(); // 执行目标方法
        } finally {
            // 无论是否抛异常，都记录日志
            long costTime = System.currentTimeMillis() - startTime;
            double costSeconds = costTime / 1000.0; // 转换为秒
            String methodName = joinPoint.getSignature().getName();
            String executeTime = sdf.get().format(new Date());
            // 日志格式严格匹配要求
            logger.info("[INFO] [{}] {} method was executed and spent {} second.",
                    executeTime, methodName, costSeconds);
        }
        return result;
    }


    // 环绕通知：处理 @NPECatch 标记的异常
    @Around("withinNPECatchPointcut() || annotationNPECatchPointcut()")
    public Object handleNPEException(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            // 输出异常信息到控制台
            System.err.println("Method [" + methodName + "] exception: " + e.getMessage());
            // 如需继续抛出异常，保留下面一行；如需吞掉异常，注释掉
            throw e;
        }
    }
}